import math

from insts.inst import NoOperandInstruction, BranchInstruction
from jthread.frame import JFrame


class CMP(NoOperandInstruction):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = None
        self.operation = None

    def execute(self, frame: JFrame):
        stack = frame.getOperandStack()
        var1 = None
        var2 = None
        if self.kind == "int":
            val2 = stack.popInt()
            val1 = stack.popInt()
        elif self.kind == "float":
            val2 = stack.popFloat()
            val1 = stack.popFloat()
        elif self.kind == "long":
            val2 = stack.popLong()
            val1 = stack.popLong()
        elif self.kind == "double":
            val2 = stack.popDouble()
            val1 = stack.popDouble()
        else:
            raise ValueError("Invalid type for CMP instruction")

        if self.operation == "fcmpl":
            if math.isnan(val1) or math.isnan(val2):
                stack.pushInt(-1)
                return
        if self.operation == "fcmpg":
            if math.isnan(val1) or math.isnan(val2):
                stack.pushInt(1)
                return
        if self.operation == "dcmpl":
            if math.isnan(val1) or math.isnan(val2):
                stack.pushInt(-1)
                return
        if self.operation == "dcmpg":
            if math.isnan(val1) or math.isnan(val2):
                stack.pushInt(1)
                return

        if val1 > val2:
            stack.pushInt(1)
        elif val1 == val2:
            stack.pushInt(0)
        elif val1 < val2:
            stack.pushInt(-1)
        else:
            raise ValueError("Invalid comparison")


# 字节码指令 FCMPL FCMPG
'''
Both value1 and value2 must be of type float. The values are popped from the operand stack and undergo value set conversion (§2.8.3), resulting in value1' and value2'. A floating-point comparison is performed:

If value1' is greater than value2', the int value 1 is pushed onto the operand stack.

Otherwise, if value1' is equal to value2', the int value 0 is pushed onto the operand stack.

Otherwise, if value1' is less than value2', the int value -1 is pushed onto the operand stack.

Otherwise, at least one of value1' or value2' is NaN. The fcmpg instruction pushes the int value 1 onto the operand stack and the fcmpl instruction pushes the int value -1 onto the operand stac
'''


class LCMP(CMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "long"
        self.operation = "cmp"


class FCMPG(CMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.operation = 'fcmpg'
        self.kind = "float"


class FCMPL(CMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.operation = 'fcmpl'
        self.kind = "float"


class DCMPG(CMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.operation = 'dcmpg'
        self.kind = "double"


class DCMPL(CMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.operation = 'dcmpl'
        self.kind = "double"


class IFCMP(BranchInstruction):

    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "int"
        self.operation = "cmp"

    def execute(self, frame: JFrame):
        stack = frame.getOperandStack()
        val2 = None
        val1 = None
        if self.operation == "icmp":
            val2 = stack.popInt()
            val1 = stack.popInt()
        elif self.operation == "cond":
            var2 = 0
            var1 = stack.popInt()
        else:
            var2 = stack.popRef()
            var1 = stack.popRef()
            if self.kind == 'EQ' and var1 == var2:
                self.branch(frame)
                return
            elif self.kind == 'NE' and var1 != var2:
                self.branch(frame)
                return
            else:
                raise ValueError("Invalid comparison")

        if self.kind == 'EQ' and val1 == val2:
            self.branch(frame)
        elif self.kind == 'NE' and val1 != val2:
            self.branch(frame)
        elif self.kind == 'LT' and val1 < val2:
            self.branch(frame)
        elif self.kind == 'GE' and val1 >= val2:  # >=
            self.branch(frame)
        elif self.kind == 'GT' and val1 > val2:
            self.branch(frame)
        elif self.kind == 'LE' and val1 <= val2:
            self.branch(frame)
        else:
            raise ValueError("Invalid comparison")

    def branch(self, frame: JFrame):
        pc = frame.getJThread().getPC()
        nextPc = pc + self.offset
        frame.setNextPc(nextPc)


class IF_ICMP_EQ(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "EQ"
        self.operation = "icmp"


class IF_ICMP_NE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "NE"
        self.operation = "icmp"


class IF_ICMP_LT(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "LT"
        self.operation = "icmp"


class IF_ICMP_GE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "GE"
        self.operation = "icmp"


class IF_ICMP_GT(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "GT"
        self.operation = "icmp"


class IF_ICMP_LE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "LE"
        self.operation = "icmp"


class IF_EQ(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "EQ"
        self.operation = "cond"


class IF_NE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "NE"
        self.operation = "cond"


class IF_LT(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "LT"
        self.operation = "cond"


class IF_GE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "GE"
        self.operation = "cond"


class IF_GT(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "GT"
        self.operation = "cond"


class IF_LE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "LE"
        self.operation = "cond"


class IF_ACMP_EQ(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "EQ"
        self.operation = "ref"

class IF_ACMP_NE(IFCMP):
    def __init__(self, opcode):
        super().__init__(opcode)
        self.kind = "NE"
        self.operation = "ref"